//
//  CGImageDrawCanvas.swift
//  DrawTest
//
//  Created by linhan on 16/8/1.
//  Copyright © 2016年 linhan. All rights reserved.
//

import Foundation
import UIKit


enum DrawMode
{
    case Draw
    case Erase
}

protocol CGImageDrawCanvasDelegate:NSObjectProtocol
{
    func CGImageDrawCanvasDrawBegan(_ canvas:SWDrawMaskCanvas, point:CGPoint)
    func CGImageDrawCanvasDrawMoved(_ canvas:SWDrawMaskCanvas, point:CGPoint)
    func CGImageDrawCanvasDrawEnded(_ canvas:SWDrawMaskCanvas, point:CGPoint?)
    
}

class SWDrawMaskCanvas: UIView,CustomLayerDelegateWrapperDelegate
{
    
    var blurAmount:CGFloat = 25
    var lineWidth:CGFloat = 30
    var drawAlpha:CGFloat = 0.5
    
    weak var delegate:CGImageDrawCanvasDelegate?
    
    var previousPoint:CGPoint = CGPointZero
    var currentPoint:CGPoint = CGPointZero
    
    var drawMode:DrawMode = .Draw
    
    var shapeLayer:CALayer = CALayer()
    
    private var selectedColor:UIColor = UIColor.lightGray
    //private var selectedColor:UIColor = UIColor(white: 0, alpha: 0.5)
    
    private var _delegateWrapper:CustomLayerDelegateWrapper = CustomLayerDelegateWrapper()
    
    private var _image:UIImage?
    private var _temp:UIImage?
    
    deinit
    {
        print("DEINIT CGImageDrawCanvas")
    }
    
    override init(frame: CGRect)
    {
        super.init(frame: frame)
        
        _delegateWrapper.delegate = self
        
        shapeLayer.delegate = _delegateWrapper
        shapeLayer.frame = bounds
        shapeLayer.shouldRasterize = true
        layer.addSublayer(shapeLayer)
        
        shapeLayer.drawsAsynchronously = true
        layer.drawsAsynchronously = true
        
        backgroundColor = UIColor.white
    }
    
    required init(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    
    override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?)
    {
        super.touchesBegan(touches, with: event)
        if let touch:UITouch = touches.first
        {
            let point:CGPoint = touch.location(in: self)
            previousPoint = point
            currentPoint = point
            
            if drawMode == .Draw {
                drawLineNew()
            }else{
                eraseLine()
            }
            
            delegate?.CGImageDrawCanvasDrawBegan(self, point: point)
        }
    }
    
    private var _lastTime:Double = 0
    override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?)
    {
        super.touchesMoved(touches, with: event)
        
        
        if let touch:UITouch = touches.first
        {
            let point:CGPoint = touch.location(in:self)
            currentPoint = point
            
            
            if drawMode == .Draw {
                drawLineNew()
            }else{
                
                let now = NSDate().timeIntervalSince1970
                if now - _lastTime > 0.04
                {
                    _lastTime = now
                    eraseLine()
                }
                
            }
            
            delegate?.CGImageDrawCanvasDrawMoved(self, point: point)
        }
    }
    
    override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?)
    {
        super.touchesEnded(touches, with: event)
        if let touch:UITouch = touches.first
        {
            let point:CGPoint = touch.location(in:self)
            currentPoint = point
            
            if drawMode == .Draw {
                drawLineNew()
                
            }else{
                eraseLine()
            }
            
            delegate?.CGImageDrawCanvasDrawEnded(self, point: point)
        }
    }
    
    override func touchesCancelled(_ touches: Set<UITouch>, with event: UIEvent?) {
        super.touchesCancelled(touches, with: event)
        delegate?.CGImageDrawCanvasDrawEnded(self, point: nil)
    }
    
    func customDrawLayer(_ layer: CALayer, inContext ctx: CGContext)
    {
        ctx.setShadow(offset: CGSizeMake(0, 0), blur: blurAmount, color: UIColor.yellow.cgColor)
        ctx.translateBy(x: 0, y: bounds.height)
        ctx.scaleBy(x: 1.0, y: -1.0)
        if let cg = _image?.cgImage
        {
            ctx.draw(cg, in: bounds)
        }
        
        if let cg = _temp?.cgImage
        {
            ctx.draw(cg, in: bounds)
        }
    }
    
    private func drawLineNew()
    {
        UIGraphicsBeginImageContextWithOptions(bounds.size, false, UIScreen.main.scale)
        _image?.draw(in:bounds)
        
        guard let context = UIGraphicsGetCurrentContext() else{
            return
        }
        context.setAlpha(0.1)
        context.setLineCap(.round)
        context.setStrokeColor(selectedColor.cgColor)
        context.setLineWidth(lineWidth)
        context.beginPath()
        context.move(to: previousPoint)
        context.addLine(to: currentPoint)
        context.strokePath()
        
        _image = UIGraphicsGetImageFromCurrentImageContext()
        UIGraphicsEndImageContext()
        
        
        let updateRect = getIntervalRect(point1: previousPoint, point2: currentPoint)
        
        previousPoint = currentPoint
        
        shapeLayer.setNeedsDisplayIn(updateRect)
    }
    
    private func eraseLine()
    {
        UIGraphicsBeginImageContextWithOptions(bounds.size, false, UIScreen.main.scale)
        _image?.draw(in:bounds)
        
        guard let context = UIGraphicsGetCurrentContext() else{
            return
        }
        
        context.setShadow(offset: CGSizeMake(0, 0), blur: blurAmount, color: UIColor.yellow.cgColor)
        context.setLineCap(.round)
        context.setLineWidth(lineWidth)
        context.beginPath()
        context.setBlendMode(.clear)
        context.move(to: previousPoint)
        context.addLine(to: currentPoint)
        context.strokePath()
        _image = UIGraphicsGetImageFromCurrentImageContext()
        UIGraphicsEndImageContext()
        
        let updateRect = getIntervalRect(point1: previousPoint, point2: currentPoint)
        
        previousPoint = currentPoint
        
        shapeLayer.setNeedsDisplayIn(updateRect)
    }
    
    //重绘两点所构成的区域（如果只重绘最新点附近区域，快速移动时，前后两点的距离非常大，就会造成断层）
    private func getIntervalRect(point1:CGPoint, point2:CGPoint) -> CGRect
    {
        let radius:CGFloat = lineWidth + blurAmount
        let diagonalRect:CGRect = CGRectDiagonalRect(previousPoint, point2: currentPoint)
        let updateRect = CGRectMake(diagonalRect.x - radius, diagonalRect.y - radius, diagonalRect.width + radius * 2, diagonalRect.height + radius * 2)
        return updateRect
    }
    
    
}
